﻿#pragma once
//#include "../../../include/Point.h"
//#include "../../../include/Line.h"
//#include "../../Utils/Utils.h"
//#include<math.h>
//#include<iostream>
//#include <algorithm>
//#include <optional>
//#include "../../Constant/Constant.h"
//using namespace std;
#include<unordered_set>
#include "../src_basics/dataStructAlg.h"  //后处理调试ui专用头文件
#include "../src_basics/utils.h"

struct PPObs {
	std::vector<Line>* olines;
	double xMin_;
	double xMax_;
	double yMin_;
	double yMax_;

public:
	PPObs(std::vector<Line>& olines_, std::vector<double>& obsMinMaxXY) {
		this->olines = &olines_;
		this->xMin_ = obsMinMaxXY[0];
		this->xMax_ = obsMinMaxXY[1];
		this->yMin_ = obsMinMaxXY[2];
		this->yMax_ = obsMinMaxXY[3];
	}

	PPObs(std::vector<Line>& olines_) {
		this->olines = &olines_;
		xMin_ = xMax_ = olines->front().Pt1.x;
		yMin_ = yMax_ = olines->front().Pt1.y;
		for (const auto& line : *olines) {
			xMin_ = std::min(xMin_, std::min(line.Pt1.x, line.Pt2.x));
			yMin_ = std::min(yMin_, std::min(line.Pt1.y, line.Pt2.y));
			xMax_ = std::max(xMax_, std::max(line.Pt1.x, line.Pt2.x));
			yMax_ = std::max(yMax_, std::max(line.Pt1.y, line.Pt2.y));
		}
	}
};


// 后处理类，版本：V2.0===================================================================================================
struct DiffInfo {
	DiffInfo(bool dir, const std::vector<double> gaps) {
		dir_ = dir;	    //另一根线在左边还是右边,左边为true
		gaps_ = gaps;
	}
	double getMiterEnlarged(int i, double miter, double width) {
		if (i >= gaps_.size()) {
			std::cout << "gaps_越界" << std::endl;
			return miter;
		}
		double result = miter + 2 * (gaps_[i] / width + 1.0) * tan(PI / 8);	//22.5,PI/8
		return result;
	}
	bool dir_{ false };
	std::vector<double> gaps_;
	//double miterTimes_{ 1.0 };
};
class PostProcessingV2 {
public:
	/**
	* @brief 默认程度后处理，Minimal(type=1)
	*/
	PostProcessingV2() {};

	/**
	* @brief 给定后处理程度构造后处理对象
	* @param postMode 1:off; 2:Minimal; 3:Smooth; 4:Full; ; 0:默认值
	*/
	PostProcessingV2(int postMode) {
		setMode(postMode);
	};
	~PostProcessingV2() {
		;
	}
public:  //设置相关参数
	//主接口
	int HuglinesPostProcessing(std::vector<Line>& hugsegments, std::vector<Line>& fixLines, const std::vector<PPObs>& obss);
	void setMode(int postMode) {
		switch (postMode) {
		case 1:		//off模式		处理（尖角，miter）；不处理（wireSpacing，斜线）。
			allowDRCs_ = true;
			enlargeBd_ = true;
			fixHalfObsLine_ = false;
			mustBe135_ = false;
			fixBdOnly_ = true;
			fixCuteAngel_ = true;
			fixMiterLine_ = true;
			remainTimes_ = 1;
			maxSteps_ = 2;
			break;
		case 2:		//minimal模式	处理（尖角，miter，wireSpacing，不贴障碍物的斜线）；不处理（贴障碍物的斜线）（默认参数）。
			allowDRCs_ = false;
			enlargeBd_ = true;
			fixHalfObsLine_ = true;
			mustBe135_ = false;
			fixBdOnly_ = true;
			fixCuteAngel_ = true;
			fixMiterLine_ = true;
			remainTimes_ = 1;
			maxSteps_ = 2;
			break;
		case 3:		//smooth模式	处理（尖角，miter，wireSpacing，不贴障碍物的斜线）；不处理（贴障碍物的斜线）；减少拐弯数量。
			allowDRCs_ = false;
			enlargeBd_ = true;
			fixHalfObsLine_ = true;
			mustBe135_ = false;
			fixBdOnly_ = false;
			fixCuteAngel_ = true;
			fixMiterLine_ = true;
			remainTimes_ = 3;
			maxSteps_ = 2;
			break;
		case 4:		//full模式		处理（尖角，miter，wireSpacing，所有斜线）。
			allowDRCs_ = false;
			enlargeBd_ = true;
			fixHalfObsLine_ = true;
			mustBe135_ = true;
			fixBdOnly_ = false;
			fixCuteAngel_ = true;
			fixMiterLine_ = true;
			remainTimes_ = 3;
			maxSteps_ = 2;
			break;
		default:	// 默认模式，与无参构造的初始化相同，与Minimal模式（type=2）相同
			;
		}
	}

	//配置接口的各种属性
	//1.基本参数（算法必须设置的参数）
	void setMiter(double value) { miter_ = value; };
	void setWireSpacing(const std::vector<double>& spaces) { WireSpacings_ = spaces; };

	//2.差分线参数（差分线后处理必须设置的参数）
	void setDiffInfo(const std::optional<DiffInfo>& info = {}) { this->diffInfo_ = info; };

	//3.可选择参数（可选择性设置的参数，设置不同值得到不同的后处理效果）
	void setAllowDRCs(bool allow) { allowDRCs_ = allow; };
	void setAllowEnlargeBoundary(bool allow) { enlargeBd_ = allow; };
	void setFixHalfObsLine(bool value) { fixHalfObsLine_ = value; };
	void setMustBe135(bool value) { mustBe135_ = value; };
	void setFixBoundaryOnly(bool value) { fixBdOnly_ = value; };
	void setFixCuteAngel(bool value) { fixCuteAngel_ = value; };
	void setFixMiterLine(bool value) { fixMiterLine_ = value; };
	void setRemainTimes(double value) { remainTimes_ = value; };
	void setLockLines(std::unordered_set<Line, v2Hash::LineHash>& lockLines) { lockLines_ = &lockLines; };

	//删除⬇️⬇️⬇️=======================================================================================
	void test_getStartAndEndIndex(int& startIndex, int& endIndex) const {	// 后处理开发调试用的函数
		startIndex = startIndex_;
		endIndex = endIndex_;
	}
	//删除⬆️⬆️⬆️=======================================================================================

private:
	//1.基本参数（算法必须设置的参数）
	double miter_ = 1;	// 默认为1
	//double WireSpacing_ = 4;
	std::vector<double> WireSpacings_;
	std::vector<Line> hugSegments_;
	const std::vector<PPObs>* obss_ = nullptr;
	std::unordered_set<Line, v2Hash::LineHash>* lockLines_ = nullptr;

	//2.差分线参数（差分线后处理必须设置的参数）
	std::optional<DiffInfo> diffInfo_;		//差分线

	//3.可选择参数（可选择性设置的参数，设置不同值得到不同的后处理效果）
	bool allowDRCs_ = false;		//允许DRC线间距异常
	bool fixHalfObsLine_ = false;	//启用修复非标准方向的半绕障线
	bool mustBe135_ = false;		//必须以135度走线，即使Hug的多边形障碍物方向不标准，也要把走线强制纠正为135度
	bool fixCuteAngel_ = true;		//是否修复尖角
	bool fixMiterLine_ = true;		//是否修复切角线
	bool fixBdOnly_ = true;			//对于非标准方向走线（即斜线），只修复边界线段，其他线段不处理
	bool enlargeBd_ = true;			//允许后处理适当放大边界，当边界线段过短时

	double remainTimes_ = 1;		//细小转角处，切角后，保留的长度设定为最短线长的miter_max倍，越大走线越光滑，不能小于1，建议不要超过10
	int maxSteps_ = 2;			    //探索过程中，起始线段最大偏移量











	//4.算法辅助参数（算法运行过程中使用的变量，此部分变量不需要设置）
	int SEIndexMoveSteps_ = 0;	    //调试的参数，起始线段和终点线段，相对fixline的首条和末条的偏移量
	int boundary1 = -1;				//记录左边界线段的索引
	int boundary2 = -1;				//记录右边界线段的索引
	int startIndex_ = -1;			//后处理的范围索引，当插入或者删除线段时，需要更新
	int endIndex_ = -1;				//后处理的范围索引，当插入或者删除线段时，需要更新
	static const std::vector<Point> direc_;  //8个标准方向,x轴方向编号为0，逆时针顺序，xy正方向角平分线编号为1，y轴正方向编号为2

private:
	//修复函数
	int fixInclinedLine2(int fixLineIndex);
	int ptToTargetLine(int S, int T, bool allowCuteAngle = false);		// 朝3个方向求交点
	int cutAngle(int preLineIndex1);	//切角，如果preLineIndex1与后面一条线夹角是直角或者锐角则切角并返回true，否则返回false，本函数不检测索引出界
	int fixMiterLine(int fixLineIndex);  //消除长度小于width * miter的线段
	int ptToPt(int S, int T);
	int inclineLineStandard(int S, int T, Line& line);

	//辅助函数
	bool isLockLine(int i);	//检查是否为锁定线段
	std::vector<Point> getInclineLineMidPt(Line& inclineLine);  //获取斜线的中间点，该点将斜线分成标准方向的两段
	double getMiter(int i);
	void setMinDistanceMinRemainingLength(double& minDistance, double& minRemainingLength, const double& crossValue, const double& maxWidth);
	void setStartAndEndIndex(std::vector<Line>& fixLines);  // 获取待处理线段的索引范围
	bool enlargeBoundary();
	int getDeleteLineNum(int i);
	int getMovePtType(Line& line);
	//std::vector<int> getValidObs(std::vector<int>& lineIndex);
	//bool isTargetLine(int targetIndex, std::vector<int>& validObsIndex);
	int getDirection(Line line);			//获取与线段line对应的标准方向的编号
	int getNearistDirection(Line line);		//获取与线段line最接近的标准方向的编号
	int merge(int index, int range1, int range2);		//合并线段，将长度为0的线段，相邻且共线的线段合并成一条，始终返回false
	//bool lineInObstacles(Line& line);	//判断线是否穿过障碍物（有交点则穿过，无交点则判断线段中点是否在障碍物内）
	//bool pointInObstacles(Point p);		//判断点是否在障碍物内部，用射线法，交点为奇数说明在障碍物内（交点在多边形端点只算一次）
	//bool pointInLine(Point p, Line line);	//判断点是否在线上，在端点或距离端点很近时，认为在线段上，返回true

	bool lineInObstacles(Line& line);	//判断线是否穿过障碍物（有交点则穿过，无交点则判断线段中点是否在障碍物内）
	bool lineInObstacles1(Line& line);	//判断线是否穿过障碍物（有交点则穿过，无交点则判断线段中点是否在障碍物内）
	bool pointInObstacles(Point p, bool polygonIncludeOutline);		//判断点是否在障碍物内部，用射线法，交点为奇数说明在障碍物内（交点在多边形端点只算一次,增加分割点）
	bool pointInObstacles1(Point p);								//判断点是否在障碍物内部，用射线法，交点为奇数说明在障碍物内
	bool pointInObstacles2(Point p, bool polygonIncludeOutline);		//判断点是否在障碍物内部，用射线法，交点为奇数说明在障碍物内（交点在多边形端点只算一次）
	bool pointInLine(Point p, Line line, bool lineIncludeVertex);	//判断点是否在线上，在端点或距离端点很近时，认为在线段上，返回true
};

